#include "rtl.h"

VOID RtlCopyMemory(LPVOID lpvDest, const LPVOID lpvSrc, DWORD dwSize) {
  __asm__("rep movsb" ::"c"(dwSize), "S"(lpvSrc), "D"(lpvDest));
}

VOID RtlSetMemory(LPVOID lpvDest, DWORD dwVal, DWORD dwSize) {
  register DWORD dwIndex;
  if (dwVal & 0xFFFF0000) {
    for (dwIndex = 0; dwIndex < dwSize; dwIndex += 4)
      *(DWORD *)((DWORD_PTR)lpvDest + dwIndex) = dwVal;
    return;
  }
  if (dwVal & 0xFF00) {
    for (dwIndex = 0; dwIndex < dwSize; dwIndex += 2)
      *(WORD *)((DWORD_PTR)lpvDest + dwIndex) = (WORD)dwVal;
    return;
  }
  for (dwIndex = 0; dwIndex < dwSize; dwIndex++)
    *(BYTE *)((DWORD_PTR)lpvDest + dwIndex) = (BYTE)dwVal;
}

VOID RtlZeroMemory(LPVOID lpvDest, DWORD dwSize) {
  __asm__("mov $0,%%al\n"
          "rep stosb\n" ::"c"(dwSize),
          "D"(lpvDest));
}

STATIC VOID itoa(DWORD dwVal, CHAR **ppcBuffer, BYTE bBase) {
  const CHAR cHex[0x10] = {'0', '1', '2', '3', '4', '5', '6', '7',
                           '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  DWORD dwNum = dwVal % bBase;
  DWORD dwIndex = dwVal / bBase;
  if (dwIndex) {
    itoa(dwIndex, ppcBuffer, bBase);
  }
  *((*ppcBuffer)++) = cHex[dwNum];
}

STATIC DWORD atoi(CHAR **ppcBuffer) {
  BYTE bBase = 10;
  while (**ppcBuffer == ' ')
    ++*ppcBuffer;
  if (**ppcBuffer == '0') {
    if (*(++(*ppcBuffer)) == 'x') {
      bBase = 16;
      ++*ppcBuffer;
    } else
      bBase = 8;
  }
  register DWORD dwVal = 0;
  while (**ppcBuffer != ' ') {
    CHAR cChar = **ppcBuffer;
    if (!cChar)
      break;
    dwVal *= (DWORD)bBase;
    if (cChar >= '0' && cChar <= '9')
      dwVal += cChar - '0';
    else if (cChar >= 'a' && cChar <= 'f')
      dwVal += cChar - 'a' + 10;
    else if (cChar >= 'A' && cChar <= 'F')
      dwVal += cChar - 'A' + 10;
    ++*ppcBuffer;
  }
  return dwVal;
}

INT RtlVSPrintf(LPSTR lpszBuffer, const LPSTR lpszFormat, va_list lpcList) {
  CHAR *pcBuffer = lpszBuffer;
  const CHAR *pcIndex = lpszFormat;
  CHAR cIndex = *pcIndex;
  INT nArg;
  CHAR *pcArg;
  while (cIndex) {
    if (cIndex != '%') {
      *(pcBuffer++) = cIndex;
      cIndex = *(++pcIndex);
      continue;
    }
    cIndex = *(++pcIndex);
    switch (cIndex) {
    case 's':
      pcArg = va_arg(lpcList, CHAR *);
      RtlStringCopy(pcBuffer, pcArg);
      pcBuffer += RtlStringLength(pcArg);
      cIndex = *(++pcIndex);
      break;
    case 'c':
      *(pcBuffer++) = va_arg(lpcList, CHAR);
      cIndex = *(++pcIndex);
      break;
    case 'd':
      nArg = va_arg(lpcList, INT);
      if (nArg < 0) {
        nArg = 0 - nArg;
        *pcBuffer++ = '-';
      }
      itoa(nArg, &pcBuffer, 10);
      cIndex = *(++pcIndex);
      break;
    case 'x':
      nArg = va_arg(lpcList, INT);
      itoa(nArg, &pcBuffer, 16);
      cIndex = *(++pcIndex);
      break;
    default:
      break;
    }
  }
  *pcBuffer = '\000';
  return pcBuffer - lpszBuffer;
}

INT RtlSPrintf(LPSTR lpszBuffer, const LPSTR lpszFormat, ...) {
  va_list lpcList;
  register INT nRetval;
  va_start(lpcList, lpszFormat);
  nRetval = RtlVSPrintf(lpszBuffer, lpszFormat, lpcList);
  va_end(lpcList);
  return nRetval;
}

INT RtlVSScanf(LPSTR lpszSrc, const LPSTR lpszFormat, va_list lpcList) {
  CHAR *pcBuffer = lpszSrc;
  const CHAR *pcIndex = lpszFormat;
  CHAR cIndex = *pcIndex;
  CHAR *pcArg;
  while (cIndex) {
    if (cIndex != '%') {
      cIndex = *(++pcIndex);
      continue;
    }
    cIndex = *(++pcIndex);
    switch (cIndex) {
    case 's':
      pcArg = va_arg(lpcList, CHAR *);
      while (*pcBuffer == ' ')
        pcBuffer++;
      while (*pcBuffer != ' ' && *pcBuffer)
        *(pcArg++) = *(pcBuffer++);
      *pcArg = '\000';
      break;
    case 'c':
      *va_arg(lpcList, CHAR *) = *(pcBuffer++);
      break;
    case 'd':
      *va_arg(lpcList, INT *) = atoi(&pcBuffer);
      break;
    default:
      break;
    }
  }
  return pcBuffer - lpszSrc;
}

INT RtlSScanf(LPSTR lpszSrc, const LPSTR lpszFormat, ...) {
  va_list lpcList;
  register INT nRetval;
  va_start(lpcList, lpszFormat);
  nRetval = RtlVSScanf(lpszSrc, lpszFormat, lpcList);
  va_end(lpcList);
  return nRetval;
}

INT RtlStringCompare(const LPSTR lpszStr1, const LPSTR lpszStr2) {
  register DWORD dwIndex;
  for (dwIndex = 0; lpszStr1[dwIndex]; dwIndex++)
    if (lpszStr1[dwIndex] == lpszStr2[dwIndex])
      continue;
    else
      return lpszStr1[dwIndex] > lpszStr2[dwIndex] ? 1 : -1;
  return lpszStr2[dwIndex] == 0
             ? 0
             : (lpszStr1[dwIndex] > lpszStr2[dwIndex] ? 1 : -1);
}

INT RtlStringNCompare(const LPSTR lpszStr1, const LPSTR lpszStr2,
                      DWORD dwLength) {
  register DWORD dwIndex;
  for (dwIndex = 0; lpszStr1[dwIndex] && dwIndex < dwLength; dwIndex++)
    if (lpszStr1[dwIndex] == lpszStr2[dwIndex])
      continue;
    else
      return lpszStr1[dwIndex] > lpszStr2[dwIndex] ? 1 : -1;
  if (dwIndex >= dwLength)
    return 0;
  return lpszStr2[dwIndex] == 0
             ? 0
             : (lpszStr1[dwIndex] > lpszStr2[dwIndex] ? 1 : -1);
}

VOID RtlStringCopy(LPSTR lpvDest, const LPSTR src) {
  register DWORD dwIndex;
  for (dwIndex = 0; src[dwIndex]; dwIndex++)
    lpvDest[dwIndex] = src[dwIndex];
  lpvDest[dwIndex] = '\000';
}

VOID RtlStringNCopy(LPSTR lpvDest, const LPSTR src, DWORD dwLength) {
  register DWORD dwIndex;
  for (dwIndex = 0; src[dwIndex] && dwIndex < dwLength; dwIndex++)
    lpvDest[dwIndex] = src[dwIndex];
  if (dwIndex < dwLength)
    lpvDest[dwIndex] = '\000';
}

VOID RtlReplaceChar(LPSTR lpszStr, CHAR cSrc, CHAR cDest) {
  register DWORD dwIndex;
  for (dwIndex = 0; lpszStr[dwIndex]; dwIndex++)
    if (lpszStr[dwIndex] == cSrc)
      lpszStr[dwIndex] = cDest;
}

DWORD RtlStringLength(const LPSTR lpszStr) {
  register DWORD dwIndex;
  for (dwIndex = 0; lpszStr[dwIndex]; dwIndex++)
    ;
  return dwIndex;
}

VOID RtlSplitString(LPSTR *ppszList, LPSTR lpszStr, CHAR cSplit) {
  register DWORD dwIndex;
  register DWORD dwLength;
  register DWORD dwNum = 0;
  dwLength = RtlStringLength(lpszStr);
  RtlReplaceChar(lpszStr, cSplit, '\000');
  if (*lpszStr) {
    ppszList[dwNum++] = lpszStr;
  }
  for (dwIndex = 1; dwIndex < dwLength; dwIndex++) {
    if ((!lpszStr[dwIndex - 1]) && lpszStr[dwIndex]) {
      ppszList[dwNum++] = lpszStr + dwIndex;
    }
  }
  ppszList[dwNum] = NULL;
}
